package cmd

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"runtime"
	"text/template"

	ostype "github.com/dwbmio/ding-cli/pkg/setting"
	"github.com/dwbmio/ding-cli/pkg/static"
	"github.com/spf13/cobra"
	"github.com/wonderivan/logger"
)

type GenconfParams struct {
	platform    string
	scriptPath  string
	rootPath    string
	serverFiles []string
}

type GenconfHandler struct {
	p GenconfParams
}

const (
	UsageGenConf = "gen the nginx-conf for ding"
	UsageInit = "init the ding project in root dir"
)

const TEMPLATE_NAME = "nginx_template"

const NGINX_TEMPLATE = `
{{.Header}}
{{.Content}}
`

const NGINX_HTTP_TEMPLATES = `
http{
{{.HttpBase}}
{{.Gzip}}
{{.DingInit}}
{{.Servers}}
}
`

const NGINX_HTTP_SERVER_TEMPLATES = `
{{range .Server}}
	{{.}}
{{end}}`

func getFileContent(filename string, Verilog string) string {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		logger.Error(Verilog)
		panic(err)
	}
	return string(data)
}

func getFileTmplContent(filename string, log string, parms interface{}) string {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		logger.Error(log)
		panic(err)
	}
	templ, err := template.New(filename).Parse(string(data))
	bytes := new(bytes.Buffer)
	err = templ.Execute(bytes, parms)
	if err != nil {
		panic(err)
	}
	return bytes.String()
}

func getStaticFile(relpath string) string {
	file, _ := exec.LookPath(os.Args[0])
	dir, err := filepath.Abs(path.Dir(file))
	if err != nil {
		fmt.Println(err)
	}
	return path.Join(dir, relpath)
}

//生成nginx conf 的顶部
func (handle *GenconfHandler) genHeader(ret *bytes.Buffer) *bytes.Buffer {
	//template header
	var headerCont string
	const FILENAME = "header.conf"
	//ensure log file
	logfile := handle.genLog2temp("logs/error.log")
	logstu := struct {
		LogFile string
	}{
		LogFile: logfile,
	}

	switch handle.p.platform {
	case string(ostype.Drawin):
		headerCont = getFileTmplContent(getStaticFile(path.Join(static.DirConfDrawin, FILENAME)), "Read header file raise error!", logstu)
		break
	default:
		headerCont = getFileTmplContent(getStaticFile(path.Join(static.DirConfNginx, FILENAME)), "Read header file raise error!", logstu)
	}
	stu := struct {
		Header  string
		Content string
		Logfile string
	}{
		Header:  headerCont,
		Logfile: logfile,
	}

	tmpl, err := template.New(TEMPLATE_NAME).Parse(NGINX_TEMPLATE)
	if err != nil {
		panic(err)
	}
	err = tmpl.Execute(ret, stu)
	if err != nil {
		panic(err)
	}

	return ret
}

//生成nginx conf 的http部分
func (handle *GenconfHandler) genHttp(ret *bytes.Buffer) *bytes.Buffer {
	const HttpFilename = "httpbase.conf"
	const GzipFilename = "gzip.conf"
	const DingFilename = "ding.conf"

	ding := struct {
		ScriptPath string
	}{
		ScriptPath: srcPath,
	}
	//log
	acclog := handle.genLog2temp("logs/access.log")

	stu := struct {
		HttpBase string
		Gzip     string
		DingInit string
		Servers  string
	}{
		HttpBase: getFileTmplContent(getStaticFile(path.Join(static.DirConfNginx, HttpFilename)), "Read httpbase file raise error!", struct {
			LogFile string
		}{LogFile: acclog}),
		Gzip:     getFileContent(getStaticFile(path.Join(static.DirConfNginx, GzipFilename)), "Read gzip file raise error!"),
		DingInit: getFileTmplContent(getStaticFile(path.Join(static.DirConfNginx, DingFilename)), "Read ding-init file raise error!", ding),
		Servers:  handle.genServers(),
	}
	tmpl, err := template.New(TEMPLATE_NAME).Parse(NGINX_HTTP_TEMPLATES)
	if err != nil {
		panic(err)
	}
	err = tmpl.Execute(ret, stu)
	if err != nil {
		panic(err)
	}
	return ret
}

//生成http server相关的配置
func (handle *GenconfHandler) genServers() string {
	tmpl, err := template.New(TEMPLATE_NAME).Parse(NGINX_HTTP_SERVER_TEMPLATES)
	if err != nil {
		logger.Error("server files gen template failed!")
		panic(err)
	}
	//var svrContents = new([]string)
	var svrContents = make([]string, len(handle.p.serverFiles))
	for _, f := range handle.p.serverFiles {
		svrContents[len(svrContents)-1] = getFileContent(f, fmt.Sprintf("faile to get the server file <%s>", f))
	}
	bytes := new(bytes.Buffer)
	tmpl.Execute(bytes, struct {
		Server []string
	}{Server: svrContents})
	return bytes.String()
}

//总配置模板
func (handle *GenconfHandler) genConf2temp(ret *bytes.Buffer) {
	var tar = path.Join(handle.p.rootPath, static.TemplateNginxConfig)
	var _, err = os.Stat(tar)
	if os.IsExist(err) {
		os.Remove(tar)
	}
	var tempDir = path.Dir(tar)
	_, err0 := os.Stat(tempDir)
	if os.IsNotExist(err0) {
		os.Mkdir(tempDir, os.ModePerm)
	}
	ioutil.WriteFile(tar, ret.Bytes(), os.ModePerm)
	nginxConf = tar
	logger.Info("Gen nginx-conf file <%s>...", tar)
}

//配置内Log文件预创建
func (handle *GenconfHandler) genLog2temp(logfile string) string {
	tar := path.Join(handle.p.rootPath, static.DirTemplate, logfile)
	_, err := os.Stat(tar)
	if os.IsNotExist(err) {
		pdir := path.Dir(tar)
		_, err0 := os.Stat(pdir)
		if os.IsNotExist(err0) {
			os.MkdirAll(pdir, os.ModePerm)
		}
		_, err = os.Create(tar)
		if err != nil {
			logger.Error(err)
			panic(fmt.Sprintf("gen logfile<%s> raise error!", tar))
		}
	}

	return tar
}

//gen_nginxconf执行入口
func (handle *GenconfHandler) Run(p GenconfParams) {
	srcPath = path.Join(rootPath, static.DirApp)
	handle.p = p
	buf := new(bytes.Buffer)
	buf = handle.genHeader(buf)
	buf = handle.genHttp(buf)

	handle.genConf2temp(buf)

	//mime.types
	mf := path.Join(handle.p.rootPath, static.DirTemplate, "mime.types")
	file, _ := os.OpenFile(path.Join(handle.p.rootPath, static.DirStatic, "mime.types"), 2, 0666)
	src1, err1 := os.Create(mf)
	if err1 != nil {
		logger.Error("create template nginx mime.types file failed!")
		panic(err1)
	}
	io.Copy(file, src1) // 把文件file, 写入src1文件
	logger.Info("Copy mime.type file <%s>...", mf)
	logger.Info("Gen nginx conf success!")
}

func GenNginxConf() *cobra.Command {
	var genCmd = &cobra.Command{
		Use:     "genconf",
		Version: "v0.0.1",
		Short:   UsageGenConf,
		Long:    UsageGenConf,
		Run: func(cmd *cobra.Command, args []string) {
			var handle = &GenconfHandler{}
			handle.Run(GenconfParams{platform: string(runtime.GOOS), scriptPath: srcPath, serverFiles: servers, rootPath: rootPath})
		},
	}
	genCmd.Flags().StringVarP(&rootPath, "rootPath", "r", "", "path to gen the nginx_conf, default is os.getwd()")
	genCmd.Flags().StringSliceVarP(&servers, "servers_conf", "c", []string{}, "the server config for http")
	genCmd.MarkFlagRequired("rootPath")
	genCmd.MarkFlagRequired("servers_conf")
	return genCmd
}
